﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Markup;

namespace Songhay.Silverlight.Mvvm
{
    using Songhay.Models;

    /// <summary>
    /// Static memebers for Silverlight Applications
    /// </summary>
    public static class ApplicationUtility
    {
        /// <summary>
        /// Gets a resource of the specified type
        /// from the Resource Stream.
        /// </summary>
        /// <typeparam name="TResource">The type of the resource.</typeparam>
        /// <param name="relativeUri">The relative URI.</param>
        public static TResource GetFromResourceStream<TResource>(string relativeUri)
        {
            var appStream = Application
                .GetResourceStream(new Uri(relativeUri, UriKind.Relative));
            using(var reader = new StreamReader(appStream.Stream))
            {
                var xaml = reader.ReadToEnd();
                var o = (TResource)XamlReader.Load(xaml);
                return o;
            }
        }

        /// <summary>
        /// Gets a resource of the specified type
        /// from the Resource Stream.
        /// </summary>
        /// <typeparam name="TResource">The type of the resource.</typeparam>
        /// <param name="resource">The resource.</param>
        public static PackedResource<TResource> GetFromResourceStream<TResource>(PackedResource resource)
        {
            if(resource == null)
                throw new ArgumentNullException("resource", "The specified resource is null");

            var resourceOfT = ApplicationUtility.GetPackedResource<TResource>(resource);

            var relativeUri = new Uri(resource.Key, UriKind.Relative);
            var appStream = Application.GetResourceStream(relativeUri);
            using(var reader = new StreamReader(appStream.Stream))
            {
                var xaml = reader.ReadToEnd();
                var o = (TResource)XamlReader.Load(xaml);

                resourceOfT.XamlObject = o;
                resourceOfT.XamlString = xaml;
            }

            return resourceOfT;
        }

        /// <summary>
        /// Gets the packed resource.
        /// </summary>
        /// <typeparam name="TResource">The type of the resource.</typeparam>
        /// <param name="resource">The resource.</param>
        public static PackedResource<TResource> GetPackedResource<TResource>(PackedResource resource)
        {
            if(resource == null)
                throw new ArgumentNullException("resource", "The specified resource is null");

            return new PackedResource<TResource>
            {
                DisplayName = resource.DisplayName,
                Key = resource.Key,
                Name = resource.Name
            };
        }

        /// <summary>
        /// Gets the Resource display text.
        /// </summary>
        /// <param name="text">The text.</param>
        public static string GetResourceDisplayText(string text)
        {
            text = Uri.UnescapeDataString(text)
                .Split('/').Last()
                .Replace(".xaml", string.Empty)
                .Replace("-", ": ")
                .Replace("+", " ");

            return text;
        }

        /// <summary>
        /// Gets the name of the resource.
        /// </summary>
        /// <param name="text">The text.</param>
        /// <returns></returns>
        public static string GetResourceName(string text)
        {
            if(string.IsNullOrEmpty(text)) return "[unknown resource name]";

            return text
                .Split('/').Last()
                .Replace(".xaml", string.Empty);
        }

        /// <summary>
        /// Lists the packed resources.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        public static IEnumerable<PackedResource> ListPackedResources(Assembly dll)
        {
            var list = new List<PackedResource>();

            if(dll == null) return list;

            var simpleAsmName = new AssemblyName(dll.FullName).Name;
            var resourceName = simpleAsmName + ".g";

            Stream stream = null;
            try
            {
                stream = dll.GetManifestResourceStream(resourceName);

                /*
                    This Silverlight block is based on
                    “Enumerating Resources in Silverlight”
                    [http://charlesstrahan.com/blog/2010/05/05/enumerating-resources-in-silverlight/]
                */
                var manager = new ResourceManager(resourceName, dll);

                var dummy = manager.GetStream("__dummy__"); // Need to do this, otherwise next line will fail...

                using(var set = manager.GetResourceSet(
                    Thread.CurrentThread.CurrentUICulture, false, true))
                {
                    var sb = new StringBuilder();
                    var enumerator = set.GetEnumerator();
                    while(enumerator.MoveNext())
                    {
                        var key = enumerator.Key.ToString();

                        if(key.StartsWith("packedxaml/"))
                        {
                            var resource =
                                new PackedResource
                                {
                                    DisplayName = ApplicationUtility
                                        .GetResourceDisplayText(key),

                                    Key = sb.AppendFormat(
                                        "/{0};Component/{1}",
                                            simpleAsmName, key).ToString(),

                                    Name = ApplicationUtility
                                        .GetResourceName(key)
                                };
                            list.Add(resource);
                            sb.Clear();
                        }
                    }
                }
            }
            finally
            {
                if(stream != null) stream.Dispose();
            }

            return list.OrderBy(i => i.Key);
        }

    }
}
